ГПОУ ТО "ДКИТ"

Программирование протоколов TCP, UDP, SPX, IPX

Функция WSAStartup инициализирует библиотеку WS2_32.DLL для использования процессом.

int WSAStartup (

WORD wVersionRequested,

LPWSADATA lpWSAData

);

wVersionRequested - Наивысшая версия Windows Sockets, которую вызвавший может использовать.

lpWSAData - Указатель на память структуры WSADATA для вывода подробной информации о Windows Sockets.

Функция WSAStartup возвращает ноль при удачном выполнении. Иначе, возвращает код ошибки.

Приложение должно вызывать одну функцию WSACleanup для каждой удачной функции WSAStartup.

4.1.1.2. Функция WSACleanup

Функция WSACleanup отключает применение библиотеки WS2_32.DLL.

int WSACleanup(void);

Функция не имеет параметров.

Возвращаемое значение ноль, если операция удалась. Иначе, возвращаемое значение равно SOCKETJERROR.

4.1.1.З. Функция getaddrinfo

Функция getaddrinfo управляет протокольно-независимой трансляцией из имени хоста к адресу.

int getaddrinfo(

const char* nodename,

const char* servname,

const struct addrinfo* hints,

struct addrinfo** res

);

nodename - указатель на строку, содержащую имя хоста или его IP- адрес.

servname - указатель на строку, содержащую имя сервиса или номер порта.

hints - указатель на структуру addrinfo, которая содержит информацию о совместимом типе сокета.

res - указатель на список одной или нескольких структур addrinfo, содержащих полную информацию о хосте.

При удачном выполнении возвращает ноль. Иначе возвращает ненулевое значение, т.е. код ошибки.

4Л. 1.4. Функция freeaddrinfo

Функция freeaddrinfo освобождает адресное пространство, которое функция getaddrinfo динамически заняла для структуры addrinfo.

void freeaddrinfo(

struct addrinfo* ai

) ;

ai - указатель на список одной или нескольких структур addrinfo для освобождения.

Эта функция не возвращает значений.

4Л. 1.5. Функция socket

Функция socket создает сокет, который прикреплен к специальному сервису.

SOCKET socket( int af, int type, int protocol

);

af - семейство адресов.

type - тип для нового сокета. SOCK_STREAM - логический канал или SOCK_DGRAM - дейтаграммы.

protocol - протокол, который будет использован с сокетом, определенный в семействе адресов.

Если без ошибок, функция socket вернет номер нового сокета. Иначе, значение, равное INVALID_SOCKET будет возвращено.

4.1.1.6. Функция bind

Функция bind закрепляет локальный адрес с сокетом, int bind(

SOCKET s,

const struct sockaddr* name, int namelen

) ;

s - идентификатор незакрепленного сокета.

name - адрес, к которому будет закреплен сокет из структуры sockaddr.

namelen - длина значения в параметре name, в байтах.

Если нет ошибок, функция bind вернет ноль. Иначе она вернет SOCKET_ERROR.

4Л. 1.7. Функция closesocket

Функция closesocket закрывает существующий сокет, int closesocket(

SOCKET s

) ;

s - сокет для закрытия.

Если нет ошибок, функция closesocket вернет ноль. Иначе она вернет значение, равное SOCKETJERROR.

4.1.1.8. Функция listen

Функция listen помещает сокет в состояние, в котором он ожидает входящие соединения.

int listen(

SOCKET s,

int backlog

) ;

s - закрепленный и несоединенный сокет.

backlog - максимальное число входящих подключений. SOMAXCONN - максимальное значение.

Если нет ошибок, функция listen вернет ноль. Иначе она вернет значение, равное SOCKET_ERROR.

4.1.1.9. Функция connect

Функция connect осуществляет соединение к сокету.

int connect(

SOCKET s,

const struct sockaddr* name, int namelen

);

s - несоединенный сокет.

name - имя сокета в структуре sockaddr, с которым будет соединение, namelen - длина name, в байтах.

Если нет ошибок, функция connect вернет ноль. Иначе она вернет значение, равное SOCKET_ERROR.

4.1.1.10. Функция recv

Функция recv принимает пакеты из соединенного или закрепленного сокета.

int recv(

SOCKET s,

char* buf,

int len,

int flags

) ;

s - соединенный сокет.

buf - буфер для входящих пакетов.

len - длина buf, в байтах.

flags - флаги, определяющие работу (желательно ставить 0).

Если нет ошибок, функция recv вернет число принятых байт. Если соединение было закрыто, то функция вернет ноль. В других случаях вернет SOCKETJERROR.

4.1.1.11. Функция recvfrom

Функция recvfrom принимает дейтаграммы и значение адреса, int recvfrom (

SOCKET s, char* buf, int len, int flags,

struct sockaddr* from, int* fromlen

);

s - закрепленный сокет.

buf - буфер для входящих пакетов.

len - длина buf, в байтах.

flags - флаги, определяющие работу (желательно ставить 0).

from - указатель на структуру sockaddr, в которой содержится информация об отправителе.

fromlen - указатель на размер from, в байтах.

Если нет ошибок, функция recvfrom вернет число принятых байт. Если соединение было закрыто, то функция вернет ноль. В других случаях вернет SOCKET_ERROR.

4.1.1.12. Функция send

Функция send отправляет пакеты соединенному сокету, int send(

SOCKET s, const char* buf, int len,

int flags

) ;

flags - флаги, определяющие работу (желательно ставить 0).

Если нет ошибок, функция send вернет число отправленных байт, которое может быть меньше чем значение 1еп. В других случаях вернет SOCKETJERROR.

4.1.1.13. Функция sendto

Функция sendto отправляет пакеты в определенное местоположение, int sendto(

SOCKET s, const char* buf, int len, int flags,

const struct sockaddr* to,

int tolen

);

s - идентификатор сокета (возможно соединенного), buf - буфер, содержащий пакеты для отправки, len - длина buf, в байтах.

flags - флаги, определяющие работу (желательно ставить 0). to - указатель на структуру sockaddr, в которой содержится информация о получателе.

tolen - размер to, в байтах.

Если нет ошибок, функция send вернет число отправленных байт, которое может быть меньше чем значение 1еп. В других случаях вернет SOCKETJERROR.

4.1.1.14. Функция accept

Функция accept ожидает входящих соединений для сокета.

SOCKET accept(

SOCKET s,

struct sockaddr* addr, int* addrlen

);

s - прослушиваемый сокет.

addr - указатель на структуру sockaddr, в которой содержится информация о клиенте.

addrlen - размер addr, в байтах.

Если нет ошибок, функция accept вернет значение типа SOCKET для нового сокета, связанного с соединением.

Структура WSADATA содержит информацию о Windows Sockets параметрах.

typedef struct WSAData {

WORD wVersion;

WORD wHighVersion;

char szDescription[WSADESCRIPTION LEN+1]; char szSystemStatus[WSASYS STATUS LEN+1]; unsigned short iMaxSockets; unsigned short iMaxUdpDg; char FAR* lpVendorlnfо;

} WSADATA, * LPWSADATA /

wVersion - версия Windows Sockets библиотеки Ws2_32.dll для использования.

wHighVersion - наивысшая версия Windows Sockets библиотеки .dll. Нормально если совпадает с wVersion.

szDescription - строка с кратким описанием библиотеки Ws2_32.dll.

szSystemStatus - строка, содержащая состояние библиотеки Ws2_32.dll.

iMaxSockets - это значение игнорируется (оставлено для старых версий).

iMaxUdpDg - это значение игнорируется (оставлено для старых версий).

lpVendorlnfo - это значение игнорируется (оставлено для старых версий).

4.1.2.2. Структура addrinfo

Структура addrinfo используется для функции getaddrinfo для получения информации о хост- адресе.

typedef struct addrinfo { int ai flags; int ai family; int ai socktype; int ai protocol; size_t ai addrlen; char* ai canonname; struct sockaddr* ai addr; struct addrinfo* ai next;

} addrinfo;

ai_flags - флаги для функции getaddrinfo. AI_PASSIVE, AI_CANONNAME и AI_NUMERICHOST.

ai_family - семейство протоколов.

ai_socktype - тип сокета. SOCK_RAW, SOCK_STREAM, или SOCK_DGRAM.

ai_protocol - тип протокола.

ai_addrlen - длина ai_addr, в байтах.

ai_canonname - каноническое имя хоста.

ai_addr - указатель на структуру sockaddr.

ai_next - указатель на следующую структуру addrinfo. Если NULL, то структура addrinfo была последней в списке.

4.1.2.3. Структура sockaddr

Структура sockaddr содержит информацию о хосте, struct sockaddr {

u_short sa_family; char sa_data[14];

};

sa_family - семейство протоколов.

sa_data - информация о хосте.

4.1.3. Использование протоколов

При программировании протоколов необходимо подключить библиотеку WS2_32.DLL при помощи функции WSAStartup. Затем получаем информацию об интересующем адресе функцией getaddrinfo, если мы используем протоколы семейства TCP/IP. Создаем сокет с параметрами нашего протокола с помощью функции socket.

Если мы используем сервер, то созданный сокет необходимо закрепить за определенным локальным адресом при помощи функции bind, затем вызвать функцию listen для этого сокета с заданным количеством соединений, если мы используем логический канал. Вызываем функцию accept, которая ожидает соединения с сервером и при входящем подключении создает новый сокет для логического канала. Использовать функцию recv для получения входящих пакетов логического канала или recvfrom для дейтаграмм. При использовании логического канала мы можем с помощью сервера отослать пакеты клиенту первыми через функцию send, а при дейтаграммах нам необходимо дождаться пакет от клиента, чтобы определить его адрес для функции send to.

При использовании клиента, после создания сокета, мы можем сразу подключиться к серверу функцией connect и начать передачу и прием пакетов для логического канала и дейтаграмм с использованием функций send и recv.

После завершения работы с сокетами, приложение должно отключить библиотеку WS2_32.DLL, используя функцию WSACleanup столько раз, сколько она была подключена.

Простейший сервер использует локальный адрес и порт с номером 1212. Его задачей является ожидание подключения клиента и приём от него пакетов длиной 512, после приема каждого пакета отсылать строку “ОК!!!” в качестве подтверждения.

//--------------------------------------------------

#include

#include

#include

#pragma hdrstop

//--------------------------------------------------

#pragma argsused int main()

{

ADDRINFO fjhints, *f_addrinfo;

SOCKET f_socket;

memset(&f_hints, 0, sizeof(ADDRINFO));

fjhints.ai_family=PF_INET;

f^hints.ai_socktype=SOCK_STREAM; f_hints.ai_flags=AI_NUMERICHOST | AI^PASSIVE;

WSADATA f_wsadata;

if(WSAStartup(MAKEWORD(2, 2), Sf^wsadata)!-0) return 0;

if(getaddrinfo("0.0.0.0", "1212", &f_hints, &f_addrinfo)!=0) return 0;

if(f_addrinfo->ai_family!=PF_INET && f_addrinfo- >ai_family!=PF_INET6)

{

freeaddrinfo(f^addrinfo); return 0;

}

f_socket=socket(f_addrinfo->ai_family, f^addrinfo- >ai_socktype, f_addrinfo->ai_protocol); if(f_socket==INVALID_SOCKET)

{

freeaddrinfo(f_addrinfo); return 0;

}

if(bind(f_socket, f_addrinfo->ai_addr, f^addrinfo- >ai_addrlen)==SOCKET_ERROR

II listen(f_socket, 64)==SOCKET_ERROR)

{

closesocket(f_socket); freeaddrinfo(f_addrinfo); return 0;

}

freeaddrinfo(f_addrinfo);

SOCKADDR_STORAGE f_from; int f^fromlen; while (1)

{

SOCKET f_socket2; f fromlen=sizeof(f from);

f_socket2=accept(f_socket,

(LPSOCKADDR)&f_from, Sf^fromlen);

if(f_socket2==INVALID_SOCKE T) break;

char f_data[512]; while(1)

{

memset(f_data, 0, 512);

int f_size=recv(f_socket2, f_data, 512, 0);

if(f_size

printf("%s/n", f_data);

send(f_socket2, "OK!!!", 6, 0);

}

closesocket(f_socket2);

}

closesocket(f_socket);

WSACleanup(); return 0;

}

//--------------------------------------------------

4.1.4.2. Пример фрагмента программы клиента с протоколом TCP

Клиент соединяется с сервером и передает пакет с введенной строкой, затем высвечивает на экран строку с подтверждением “ОК!!!” от сервера.

//--------------------------------------------------

#include

#include

#include

#pragma hdrstop

//--------------------------------------------------

#pragma argsused int main()

{

ADDRINFO f_hints, *f_addrinfo;

SOCKET f_socket;

memset(&f_hints, 0, sizeof(f_hints)); f^hints.ai_family=PF_INET; f^hints.ai_socktype=SOCK_STREAM;

WSADATA f_wsadata;

if(WSAStartup(MAKEWORD(2, 2), Sf^wsadata)!-0) return 0;

if(getaddrinfo("127.0.0.1", "1212", &f_hints, &f_addrinfo)!=0) return 0;

f_socket=socket(f_addrinfo->ai_family, f^addrinfo- >ai_socktype, f_addrinfo->ai_protocol); if(f_socket==INVALID_SOCKET)

{

freeaddrinfo(f^addrinfo); return 0;

}

if(connect(f_socket, f_addrinfo->ai_addr, f_addrinfo->ai_addrlen)==SOCKET_ERROR)

{

freeaddrinfo(f^addrinfo); closesocket(f^socket); return 0;

}

freeaddrinfo(f_addrinfo) ; char f_data[512]; while (1)

{

gets(f_data);

if(send(f_socket, f_data, strlen(f_data)+1,

0)!=SOCKET_ERROR)

{

recv(f_socket, f_data, 512, 0); puts(f_data);

}

else

{

WSACleanup() ; return 0;

}

}

return 0;

}

//--------------------------------------------------

4.1.4.З. Комментарии к примерам

Для написания приложений с другими протоколами достаточно изменить их идентификаторы в функциях и ввести соответствующие адреса.

Текущие примеры используют файл включения ws2tcpip.h, который также используется для протокола UDP. Если вы собираетесь использовать протоколы SPX и IPX, то необходимо включить в программу файл wsipx.h, содержащий дополнительные структуры и описания.

Примеры написаны на Borland Developer Studio 2012. Для подробной справки используйте справочную систему вашего компилятора.